package com.bootdo.common.utils;

import com.bootdo.app.service.impl.AppLoginServiceImpl;
import net.coobird.thumbnailator.Thumbnails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;

/**
 * @program: bootdo
 * @packageName: com.bootdo.common.utils
 * @className: PicUtils
 * @description: 图片压缩
 * @create: 2020-04-15 11:43
 */
public class PicUtils {

    private static Logger logger = LoggerFactory.getLogger(PicUtils.class);

    /**
     * 根据指定大小和指定精度压缩图片
     *
     * @param srcPath
     *            源图片地址
     * @param desPath
     *            目标图片地址
     * @param desFileSize
     *            指定图片大小，单位kb
     * @param accuracy
     *            精度，递归压缩的比率，建议小于0.9
     *  @param desMaxWidth
     *             目标最大宽度
     *  @param desMaxHeight
     *             目标最大高度
     * @return 目标文件路径
     */
    public static String commpressPicForScale(String srcPath, String desPath,
                                              long desFileSize, double accuracy,int desMaxWidth,int desMaxHeight) {
        if (StringUtils.isEmpty(srcPath) || StringUtils.isEmpty(srcPath)) {
            return null;
        }
        if (!new File(srcPath).exists()) {
            return null;
        }
        try {
            File srcFile = new File(srcPath);
            long srcFileSize = srcFile.length();
            logger.info("源图片：" + srcPath + "，大小：" + srcFileSize / 1024
                    + "kb");
            //获取图片信息
            BufferedImage bim = ImageIO.read(srcFile);
            int srcWidth = bim.getWidth();
            int srcHeight = bim.getHeight();

            //先转换成jpg
            Thumbnails.Builder builder = Thumbnails.of(srcFile).outputFormat("jpg");

            // 指定大小（宽或高超出会才会被缩放）
            if(srcWidth > desMaxWidth || srcHeight > desMaxHeight) {
                builder.size(desMaxWidth, desMaxHeight);
            }else{
                //宽高均小，指定原大小
                builder.size(srcWidth,srcHeight);
            }

            // 写入到内存//字节输出流（写入到内存）
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            builder.toOutputStream(baos);

            // 递归压缩，直到目标文件大小小于desFileSize
            byte[] bytes = commpressPicCycle(baos.toByteArray(), desFileSize, accuracy);

            // 输出到文件
            File desFile = new File(desPath);
            FileOutputStream fos = new FileOutputStream(desFile);
            fos.write(bytes);
            fos.close();

            logger.info("目标图片：" + desPath + "，大小" + desFile.length() / 1024 + "kb");
            logger.info("图片压缩完成！");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return desPath;
    }

    private static byte[] commpressPicCycle(byte[] bytes, long desFileSize, double accuracy)  {
        BufferedImage bim = null;
        ByteArrayOutputStream baos = null;
        try {
            // File srcFileJPG = new File(desPath);
            long srcFileSizeJPG = bytes.length;
            // 2、判断大小，如果小于500kb，不压缩；如果大于等于500kb，压缩
            if (srcFileSizeJPG <= desFileSize * 1024) {
                return bytes;
            }
            // 计算宽高
            bim = ImageIO.read(new ByteArrayInputStream(bytes));
            int srcWdith = bim.getWidth();
            int srcHeigth = bim.getHeight();
            int desWidth = new BigDecimal(srcWdith).multiply(
                    new BigDecimal(accuracy)).intValue();
            int desHeight = new BigDecimal(srcHeigth).multiply(
                    new BigDecimal(accuracy)).intValue();

            //字节输出流（写入到内存）
            baos = new ByteArrayOutputStream();
            Thumbnails.of(new ByteArrayInputStream(bytes)).size(desWidth, desHeight).outputQuality(accuracy).toOutputStream(baos);
            byte[] by = baos.toByteArray();
            return commpressPicCycle(by, desFileSize, accuracy);
        } catch (Exception e) {
            logger.error("【图片压缩】msg=图片压缩失败!", e);
        } finally {
            try {
                baos.flush();
                baos.close();
                bim.flush();
            } catch (Exception e) {
                e.printStackTrace();
                baos = null;
                bim = null;
            }

        }
        return null;
    }


    /**
     * 根据指定大小压缩图片
     *
     * @param imageBytes  源图片字节数组
     * @param desFileSize 指定图片大小，单位kb
     * @param imageId     影像编号
     * @return 压缩质量后的图片字节数组
     */
    public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize, String imageId) {
        if (imageBytes == null || imageBytes.length <= 0 || imageBytes.length < desFileSize * 1024) {
            return imageBytes;
        }
        long srcSize = imageBytes.length;
        double accuracy = getAccuracy(srcSize / 1024);
        ByteArrayInputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        try {
            while (imageBytes.length > desFileSize * 1024) {
                inputStream = new ByteArrayInputStream(imageBytes);
                outputStream = new ByteArrayOutputStream(imageBytes.length);
                Thumbnails.of(inputStream)
                        .scale(accuracy)
                        .outputQuality(accuracy)
                        .toOutputStream(outputStream);
                imageBytes = outputStream.toByteArray();
            }
            logger.info("【图片压缩】imageId={} | 图片原大小={}kb | 压缩后大小={}kb",
                    imageId, srcSize / 1024, imageBytes.length / 1024);
        } catch (Exception e) {
            logger.error("【图片压缩】msg=图片压缩失败!", e);
        } finally {
            try {
                outputStream.flush();
                outputStream.close();
                inputStream.close();
            } catch (Exception e) {
                outputStream = null;
                inputStream = null;
            }
        }
        return imageBytes;
    }

    /**
     * 自动调节精度(经验数值)
     *
     * @param size 源图片大小
     * @return 图片压缩质量比
     */
    private static double getAccuracy(long size) {
        double accuracy;
        if (size < 900) {
            accuracy = 0.85;
        } else if (size < 2047) {
            accuracy = 0.6;
        } else if (size < 3275) {
            accuracy = 0.44;
        } else {
            accuracy = 0.4;
        }
        return accuracy;
    }

    public static void main(String[] args) {
        PicUtils.commpressPicForScale("D:\\home\\9727598b8b157af71cd4b4483e44ca4.png","D:\\home\\9727598b8b157af71cd4b4483e44ca4.png", 30, 0.8,750,1334); // 图片小于1000kb
    }

}
